Skip to content

chore(release): 0.8.0 - #34

Draft
imbenrabi wants to merge 18 commits into
mainfrom
claude/project-thread-4ocdp6
Draft

imbenrabi wants to merge 18 commits into
mainfrom
claude/project-thread-4ocdp6

Conversation

@imbenrabi

@imbenrabi imbenrabi commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Ben · project thread

Before: main is at 0.7.0, and five finished changes (#29, #30, #31, #32, #33) are open as separate PRs with no release entry.

After: version 0.8.0 with one changelog entry covering all five, plus one small addition: PermissionAwareFastifyTransport now rejects a client whose startup toolsets failed to load, the follow-up #33 and #30 asked for. Docs match the new behaviour. Nothing is tagged or published.

Merge order (read first)

Base is main, but none of #29-#33 has merged yet, so this draft branch contains all five merged in (one merge conflict, in CHANGELOG.md, resolved by writing the 0.8.0 entry). Merge them first, in any order; this PR's diff then shrinks to the release commits and merges cleanly on top. Do not rebase or force-push the PR branches.

PR Change State
#29 enable_toolset/disable_toolset advertise destructiveHint: false open
#30 permission-aware transport waits for the orchestrator before connect() open
#31 disable_toolset removes the toolset's tools open
#32 opt-in http.allowedOrigins Origin check on /mcp open
#33 failed startup toolset registration fails initialize and STATIC startup (Breaking, still a draft) open

This release ships #33 as written (Strict semantics).

What the release adds beyond the five PRs

  • package.json / package-lock.json 0.7.0 to 0.8.0, and CHANGELOG.md [0.8.0] with Breaking, Added, Changed and Fixed sections and migration notes.
  • PermissionAwareFastifyTransport: isReady() false gives 500 / JSON-RPC -32603 and drops the cached bundle, as FastifyTransport does. Two new e2e tests fail without it.
  • Docs: README (startup failure behaviour), root AGENTS.md invariant 4 (loader errors stay silent, registration failures no longer do) and src/permissions/AGENTS.md. allowedOrigins, destructive hints and disable-removes-tools were already documented by their PRs.

Verification

  • npm run typecheck, npm run test:run (393 tests) and npm run build pass.
  • npm publish --dry-run: toolception@0.8.0, 64 files, 107 kB; only dist/, package.json, README.md, CHANGELOG.md and LICENSE; no src/, tests or env files.
  • npm run verify:toolception in the FMP server (main, 2.7.0) against the packed tarball: 19/21. Both failures are expected and are server work that must merge first:
    1. ALL_TOOLS does not start: the calendar and earnings toolsets both list the earnings-transcript module, so getLatestEarningsTranscripts collides (src/constants/toolSets.ts). The server needs a catalog fix, being done in a separate project thread.
    2. The dynamic smoke test should disable toolset (state only, tools remain) asserts the old disable behaviour. Fixed by server #113. (The same overlap breaks the legacy/all-tools smoke tests and the installation checks.)

Server prerequisites before the FMP server takes 0.8.0

  1. Merge server #113.
  2. Merge the catalog fix that removes the duplicate earnings-transcript module from one of calendar / earnings.
  3. After 0.8.0 is on npm: npm run verify:toolception -- --source 0.8.0 must be green, then the dependency bump PR, with the docs that say disable is "state tracking only" (docs/USAGE.md, src/endpoints/server-card.ts) changed together with it.

Release steps (Ben)

After the five PRs and this one are merged: tag v0.8.0 on main HEAD (release.yml publishes to npm and creates the GitHub Release). The command will be given in the project thread.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Jke6dnq5CNkrDFWbJSHJWr

claude added 16 commits October 10, 2026 16:51
Both meta-tools were annotated destructiveHint: true, but neither deletes
anything: they change which tools are exposed and can be reversed by the
other. Set destructiveHint: false, keep idempotentHint: true, assert the
annotations in tests and update the meta intent node.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZj3jC148r1VwBYogZpWKL
…lient

PermissionAwareFastifyTransport connected the per-client server before the
bundle's orchestrator finished preloading, so tools registered after connect
and the client got 'Method not found' on tools/list. Await isReady() first,
as FastifyTransport already does.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cv2mT1Y95UFmTFB8mZZhcL
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZj3jC148r1VwBYogZpWKL
Disabling a toolset only updated state, so clients could still call its
tools. DynamicToolManager now keeps the RegisteredTool handles returned by
registerTool and calls remove() on disable, frees the names in ToolRegistry
so the toolset can be enabled again, and rolls back a partially failed
enable the same way. Intent layer, README and the meta-tool description no
longer claim the SDK can't remove tools.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GKeDY2JCSBC6vG56EEKziV
enableToolsets() reports failures in its result instead of throwing, so
ServerOrchestrator.isReady() was true even when a startup toolset failed to
enable, and FastifyTransport ignored the result anyway. A client could connect
with only part of its tools.

initializeToolsets() now turns a failed result into initError, and the
transport answers initialize with a JSON-RPC -32603 error (HTTP 500) and drops
the bundle so the next initialize retries.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UJfs8sWB5EK8dXXjDU7Mv3
- Require the request Origin header to be a bare origin
- Type allowedOrigins as readonly string[]
- Export OriginGuard type instead of ReturnType<typeof ...>
- Cover PermissionAwareFastifyTransport invalid-entry construction

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AKosQp8PUoZijAkZ7RWHj4
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GKeDY2JCSBC6vG56EEKziV
Reviewer findings: exposure-policy refusals and duplicate startup toolset
names were made fatal by the previous commit. Only failures that happen while
registering a toolset's tools (carried as a code on the enable result) now
fail readiness. Changelog files the behaviour change as Breaking.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UJfs8sWB5EK8dXXjDU7Mv3
…g entry

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UJfs8sWB5EK8dXXjDU7Mv3
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jke6dnq5CNkrDFWbJSHJWr
…ets failed to load

Mirrors FastifyTransport: a not-ready bundle gets a 500 / -32603 on
initialize and is dropped from the cache. Docs updated for 0.8.0.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jke6dnq5CNkrDFWbJSHJWr
@imbenrabi imbenrabi self-assigned this Oct 10, 2026

Copy link
Copy Markdown
Contributor Author

TS review: pipeline-ts-reviewer (feature-pipeline) — round 1

Verdict: REQUEST_CHANGES. The permission-aware isReady() guard is correct as code and mirrors FastifyTransport, and the version bump is consistent. But the 0.8.0 changelog and docs claim a behaviour change for createPermissionBasedMcpServer that the code cannot deliver.

Reviewed: the full diff against main (head a2857cf) plus ServerOrchestrator.ts, DynamicToolManager.ts, createPermissionBasedMcpServer.ts, permissions.utils.ts, PermissionAwareFastifyTransport.ts, createMcpServer.ts, the src/index.ts exports, ModuleResolver, ClientResourceCache, package.json and package-lock.json. Static analysis only.

# Sev Location Finding
1 HIGH CHANGELOG.md [0.8.0] Breaking, first bullet Says createPermissionBasedMcpServer clients with failing startup toolsets now get -32603 / HTTP 500. False: the permission orchestrator is built with toolsets: [], createPermissionAwareBundle enables toolsets itself, partial failure still connects (failedToolsets), total failure is 403, so isReady() never goes false there.
2 MEDIUM CHANGELOG.md [0.8.0] Fixed (#30) Describes PermissionAwareFastifyTransport, which is not exported from src/index.ts, racing on background preload. Unreachable via the public factory, which awaits enableToolsets before returning.
3 MEDIUM src/permissions/AGENTS.md flow; root AGENTS.md invariant 4 Imply the isReady / 500 path covers permission clients, and contradict "partial success continues with warning".
4 MEDIUM tests/e2e/permissionBased.e2e.test.ts (new describes) Build the transport directly with hand-made bundles, so they pass while the public path is unchanged and hide finding 1. Label as transport-level, and pin the real behaviour through createPermissionBasedMcpServer.
5 LOW README.md startup note "activates with no tools" overstates it; other modules in the toolset still register ("fewer tools").
{
  "recommendation": "REQUEST_CHANGES",
  "summary": "Version bump and most changelog/doc claims match the code, but the Breaking entry (and related docs/tests) claim createPermissionBasedMcpServer clients now get a 500/-32603 on failed startup toolsets, which cannot happen because that factory enables toolsets itself, tolerates partial failure, and leaves orchestrator.isReady() always true.",
  "findings": [
    { "severity": "HIGH", "file": "CHANGELOG.md", "line": null, "issue": "Breaking entry claims permission-based clients get -32603/500 on failed startup toolsets; false for the public factory." },
    { "severity": "MEDIUM", "file": "CHANGELOG.md", "line": null, "issue": "Fixed entry for #30 describes an unexported class racing on preload; unreachable via createPermissionBasedMcpServer." },
    { "severity": "MEDIUM", "file": "src/permissions/AGENTS.md", "line": null, "issue": "Permission Flow and root invariant 4 imply the isReady/500 path covers permission clients and contradict partial-success behaviour." },
    { "severity": "MEDIUM", "file": "tests/e2e/permissionBased.e2e.test.ts", "line": null, "issue": "New e2e tests bypass the public factory and mask the changelog inaccuracy." },
    { "severity": "LOW", "file": "README.md", "line": null, "issue": "'activates with no tools' vs 'fewer tools' elsewhere." }
  ]
}

Generated by Claude Code

createPermissionBasedMcpServer enables toolsets in its bundle factory and
continues on partial failure, so the isReady() rejection does not apply.
Pin that with a public-factory test and label the transport-level tests.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jke6dnq5CNkrDFWbJSHJWr

Copy link
Copy Markdown
Contributor Author

Fixes for the round 1 TS review

All five findings were correct (I confirmed the permission flow in permissions.utils.ts: toolsets are enabled in the bundle factory, partial failure continues, all-failed throws and becomes a 403). Fixed in 0349957.

# Finding Fix
1 (HIGH) Breaking entry claimed permission-based clients get 500 / -32603 Entry now covers STATIC createMcpServer only, and says createPermissionBasedMcpServer is unaffected (partial failure still connects with failedToolsets, all failed is still 403).
2 (MEDIUM) #30 Fixed entry described an unreachable race Reworded as hardening of the transport for hand-built createPermissionAwareBundle bundles; says the public factory's behaviour is unchanged.
3 (MEDIUM) src/permissions/AGENTS.md flow and root invariant 4 implied the guard covers permission clients Both now say only a hand-built bundle can be not ready, and that createPermissionBasedMcpServer continues on partial failure. Root invariant 4 names it as the exception.
4 (MEDIUM) e2e tests bypassed the public factory The two transport tests are labelled "transport level". New e2e case through createPermissionBasedMcpServer pins the real behaviour: partial failure connects with the good toolset's tools, all failed is refused. 395 tests pass.
5 (LOW) README said "no tools" Now "fewer tools", matching the other docs.

The guard itself stays: it is a small mirror of FastifyTransport for the exported-in-spirit custom-bundle path, and the review found the code correct.


Generated by Claude Code

…ening

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jke6dnq5CNkrDFWbJSHJWr

Copy link
Copy Markdown
Contributor Author

TS review: pipeline-ts-reviewer — round 2 (head 0349957)

Verdict: COMMENT (no CRITICAL/HIGH). All five round-1 findings were fixed correctly and truthfully, every [0.8.0] changelog and doc claim checked matches the code, and the fix commit introduced nothing new. Three LOW notes:

# Sev Location Finding Fix (1c58bb8)
1 LOW CHANGELOG.md Fixed (#30) "hand-built createPermissionAwareBundle callback" implies a path consumers have; the class and helper are not exported. Reworded as internal hardening: the class is not public API and createPermissionBasedMcpServer is unchanged.
2 LOW permissionBased.e2e.test.ts "all failed" Bare rejects.toThrow() passes on any error. Now asserts /Access denied/ (the 403 body).
3 LOW permissionBased.e2e.test.ts partial failure Did not assert the failed toolset's tool is absent. Now asserts dup.ping is not listed, which also pins the #31 rollback on the public path.
{"recommendation":"COMMENT","summary":"All five round-1 findings fixed correctly and truthfully; only LOW polish remains.","findings":[{"severity":"LOW","file":"CHANGELOG.md","line":null,"issue":"#30 entry implies a user-reachable path; reword as internal hardening."},{"severity":"LOW","file":"tests/e2e/permissionBased.e2e.test.ts","line":null,"issue":"bare rejects.toThrow() in the all-failed test."},{"severity":"LOW","file":"tests/e2e/permissionBased.e2e.test.ts","line":null,"issue":"partial-failure test does not assert the failed toolset's tool is absent."}]}

All three are addressed in 1c58bb8 (typecheck clean, 395 tests pass). Round 3 follows to confirm APPROVE.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

TS review: pipeline-ts-reviewer — round 3 (head 1c58bb8)

Verdict: APPROVE. All three round-2 LOW notes are fixed, the [0.8.0] changelog and touched docs match the code, and nothing new was found at any severity. CI is green on this head.

{"recommendation":"APPROVE","summary":"All three round-2 notes are fixed (CHANGELOG #30 reads as internal hardening, the all-failed test asserts /Access denied/, the partial-failure test asserts dup.ping is absent), the 0.8.0 CHANGELOG and touched docs match the code, and nothing new was introduced.","findings":[]}

Still a draft on purpose: #29-#33 are unmerged and are included in this branch. Merge them first, then mark this ready.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants